#include <arpa/inet.h>
#include <openssl/cmac.h>
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <pcap.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include "psp.h"


int main(int argc,char **argv)
{
    int s;
    struct sockaddr_in server;
    uint8_t *in_pkt;
    char *cfg_file = DEFAULT_ENCRYPT_CFG_FILE;
    struct pkt_context pkt_ctx;
    uint8_t *out_pkt;
    int recv_size;
    int server_address_size = sizeof(server);

    if(argc != 2)
    {
        printf("Usage: %s <host address> <port> \n",argv[0]);
        exit(1);
    }

    if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0)
    {
        perror("socket()");
        exit(1);
    }

    server.sin_family      = AF_INET;            
    server.sin_port        = htons(1000);            
    server.sin_addr.s_addr = inet_addr(argv[1]); 

    pkt_ctx.max_pkt_octets = ETH_JUMBO_MAX_OCTETS;
    pkt_ctx.out_pkt = NULL;
    pkt_ctx.next_iv = PSP_INITIAL_IV;
    pkt_ctx.scratch_buf = NULL;

    /* read psp config file */
    if (get_psp_cfg(cfg_file, &pkt_ctx, PSP_MODE_ENCRYPT) != SUCCESS_RC) goto err_exit;

    /* derive psp encryption key */
    if (derive_psp_key(&pkt_ctx) != SUCCESS_RC) goto err_exit;


    pkt_ctx.in_pkt = calloc(1, pkt_ctx.max_pkt_octets);
    if (pkt_ctx.in_pkt == NULL) {
        fprintf(stderr, "calloc() failed\n");
        goto err_exit;
    }

    pkt_ctx.out_pkt = calloc(1, pkt_ctx.max_pkt_octets);
    if (pkt_ctx.out_pkt == NULL) {
        fprintf(stderr, "calloc() failed\n");
        goto err_exit;
    }

    pkt_ctx.scratch_buf = calloc(1, pkt_ctx.max_pkt_octets);
    if (pkt_ctx.scratch_buf == NULL) {
        fprintf(stderr, "calloc() failed\n");
        goto err_exit;
    }

    in_pkt = pkt_ctx.in_pkt;
    out_pkt = pkt_ctx.out_pkt;
    for(int i = 0; i < SEND_NUMBER; i++) {
        in_pkt[i] = i % 0xff;
    }
    struct timeval tv_begin, tv_end;
    for(int i = 0; i < 100; i++) {

        for(int i = 0; i < SEND_NUMBER; i++) {
            in_pkt[i] = i % 0xff;
        }

        gettimeofday(&tv_begin, NULL);
        if (pkt_ctx.psp_cfg.psp_encap == PSP_TRANSPORT) {
            transport_encap(&pkt_ctx);
        } else {
            //TODO:
            // tunnel_encap(&pkt_ctx);
        }

        /* Send the message in buf to the server */
        if (sendto(s, out_pkt, (SEND_NUMBER + PSP_TRANSPORT_ENCAP_OCTETS), 0,
                   (struct sockaddr *)&server, sizeof(server)) < 0)
        {
            perror("sendto()");
            exit(2);
        }

        if((recv_size = recvfrom(s, in_pkt, pkt_ctx.max_pkt_octets, 0, (struct sockaddr *) &server,
                                 &server_address_size)) <0)
        {
            perror("recvfrom()");
            exit(4);
        }
        // for(int i = 0; i < recv_size; i++) {
        //     printf("%02x ", in_pkt[i]);
        // }

        pkt_ctx.psp_payload_len = recv_size - sizeof(struct ipv4_hdr) - sizeof(struct udp_hdr);
        pkt_ctx.in_pkt = pkt_ctx.in_pkt + sizeof(struct ipv4_hdr) + sizeof(struct udp_hdr);
        parse_packet(&pkt_ctx);
        pkt_ctx.in_pkt = pkt_ctx.in_pkt - sizeof(struct ipv4_hdr) - sizeof(struct udp_hdr);

        gettimeofday(&tv_end, NULL);
        printf("time:%ld %ld\n",tv_end.tv_sec - tv_begin.tv_sec, tv_end.tv_usec - tv_begin.tv_usec);
    }

err_exit:
    free(in_pkt);
    free(pkt_ctx.out_pkt);
    free(pkt_ctx.scratch_buf);
    close(s);
    return 0;
}


